﻿#region License

// Created by John Batte on 04/03/2012 at 3:10 PM.
// Last code cleanup: 04/04/2012 at 3:37 PM.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.IO;
using System.Reactive.Subjects;
using System.Reflection;

using BatteCode.Fx.Diagnostics;
using BatteCode.Fx.Reactive;
using BatteCode.Fx.RegularExpressions;

namespace BatteCode.Google.Wiki
{
	/// <summary>
	/// 	Provides a wiki writer.
	/// </summary>
	public class WikiWriter : IWikiWriter
	{
		private static readonly CompiledRegex _invalidChars = @"(?i)[^a-z0-9]+";
		private readonly IFileWriter _fileWriter;
		private readonly Subject<WikiStatus> _updates = new Subject<WikiStatus>();

		/// <summary>
		/// 	Initializes a new instance of the <see cref="WikiWriter" /> class.
		/// </summary>
		/// <param name="environment"> The environment. </param>
		/// <param name="fileWriter"> The file writer. </param>
		public WikiWriter(IEnvironmentInfo environment, IFileWriter fileWriter)
		{
			_fileWriter = fileWriter;
			RootDirectory = environment.CurrentDirectory;
			StatusOutput = new DebugTextWriter();

			Update(_updates, StatusOutput, new WikiStatus { WriterState = WriterState.Idle });
		}

		#region IWikiWriter Members

		/// <summary>
		/// 	Gets or sets the root directory.
		/// </summary>
		/// <value> The root directory. </value>
		public string RootDirectory{ get; set; }

		/// <summary>
		/// 	Gets or sets the status output writer.
		/// </summary>
		/// <value> The status output writer. </value>
		public TextWriter StatusOutput{ get; set; }

		/// <summary>
		/// 	Gets the observable status updates.
		/// </summary>
		public IObservable<WikiStatus> StatusUpdates
		{
			get { return _updates; }
		}

		/// <summary>
		/// 	Writes the specified member using the provided text.
		/// </summary>
		/// <param name="member"> The member. </param>
		/// <param name="text"> The text. </param>
		public void Write(MemberInfo member, string text)
		{
			Update(_updates, StatusOutput, new WikiStatus
			{
				WriterState = WriterState.Writing,
				Data = string.Format("Writing wiki output for {0}", member.Name)
			});

			try
			{
				string path = Path.Combine(RootDirectory, GetWikiFileName(member));
				Update(_updates, StatusOutput, new WikiStatus
				{
					WriterState = WriterState.Writing,
					Data = path
				});
				_fileWriter.WriteAllText(path, text);
			}
			catch (Exception ex)
			{
				Update(_updates, StatusOutput, new WikiStatus
				{
					WriterState = WriterState.Error,
					Data = ex
				});
			}

			Update(_updates, StatusOutput, new WikiStatus { WriterState = WriterState.Idle });
		}

		#endregion

		private static string GetCompleteTypeName(Type type)
		{
			return _invalidChars.Replace(type.FullName, "-").ToLower();
		}

		private static string GetWikiFileName(MemberInfo member)
		{
			if (member.MemberType == MemberTypes.NestedType || member.MemberType == MemberTypes.TypeInfo)
				return GetTypeFileName(member as Type);

			return GetMemberFileName(member);
		}

		private static string GetMemberFileName(MemberInfo member)
		{
			string memberName = _invalidChars.Replace(member.Name, "-").ToLower().TrimStart('-');
			return string.Format("{0}-{1}.wiki", GetCompleteTypeName(member.DeclaringType), memberName);
		}

		private static string GetTypeFileName(Type type)
		{
			return string.Format("{0}.wiki", GetCompleteTypeName(type));
		}

		private static void Update(IObserver<WikiStatus> observer, TextWriter outputWriter, WikiStatus status)
		{
			outputWriter.WriteLine("Status: {0}", status);
			if (status.Data != null) outputWriter.WriteLine(status.Data);
			observer.Publish(status);
		}
	}
}